package com.firemoss.atonement.metadata
{
	import com.firemoss.atonement.metadata.inspectors.bindability.*;
	import com.firemoss.atonement.metadata.inspectors.entity.*;
	import com.firemoss.atonement.metadata.inspectors.property.*;
	
	import flash.media.Video;
	import flash.utils.Dictionary;
	
	import org.as3commons.lang.ClassUtils;

	public class DefaultMetadataBuilder implements IMetadataBuilder
	{
		private var metadataCache : Dictionary = new Dictionary();
		
		private var _bindabilityInspectorFactory:IBindabilityInspectorFactory = new DefaultBindabilityInspectorFactory()
		public function get bindabilityInspectorFactory() : IBindabilityInspectorFactory
		{
			return _bindabilityInspectorFactory
		}
		public function set bindabilityInspectorFactory( value : IBindabilityInspectorFactory ) : void
		{
			_bindabilityInspectorFactory = value
		}

		private var _entityInspectorFactory:IEntityInspectorFactory = new DefaultEntityInspectorFactory()
		public function get entityInspectorFactory() : IEntityInspectorFactory
		{
			return _entityInspectorFactory
		}
		public function set entityInspectorFactory( value : IEntityInspectorFactory ) : void
		{
			_entityInspectorFactory = value
		}
		
		private var _propertyInspectorFactory:IPropertyInspectorFactory = new DefaultPropertyInspectorFactory()
		public function get propertyInspectorFactory() : IPropertyInspectorFactory
		{
			return _propertyInspectorFactory
		}
		public function set propertyInspectorFactory( value : IPropertyInspectorFactory ) : void
		{
			_propertyInspectorFactory = value
		}
		
		public function DefaultMetadataBuilder()
		{
		}
		
		public function buildMetadata(instance:Object):AtonementMetadata
		{
			return buildMetadataForClass( ClassUtils.forInstance( instance ) );
		}
		
		public function buildMetadataForClass(clazz:Class):AtonementMetadata
		{
			if ( metadataCache[ clazz ] == null )
			{
				var md : AtonementMetadata = new AtonementMetadata();
				
				// Cache it _first_ to prevent any subsequent metadata lookups (from inspectors)
				// from causing a stack overflow
				metadataCache[ clazz ] = md;

				buildPropertyChangeEvents( md, clazz );
				buildEntityMetadata( md, clazz );
				buildPropertyMetadata( md, clazz );
			}
			
			return metadataCache[ clazz ];
		}
		
		protected function buildPropertyChangeEvents( md : AtonementMetadata, clazz : Class ) : void
		{
			md.propertyChangeEvents = bindabilityInspectorFactory.create( clazz ).inspect( clazz );
		}
		
		protected function buildEntityMetadata( md : AtonementMetadata, clazz : Class ) : void
		{
			var emd : EntityMetadata = entityInspectorFactory.create( clazz ).inspect( this, clazz );
			
			if ( emd.idPropertyName != null )
			{
				md.entityMetadata = emd
			}
		}
		
		protected function buildPropertyMetadata( md : AtonementMetadata, clazz : Class ) : void
		{
			md.propertyMetadata = propertyInspectorFactory.create( clazz ).inspect( clazz );
		}
	}
}